package base.thread_demo.demo13;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

/**
 * 同步模式之保护性暂停：
 * 即一个线程等待另一个线程的执行结果。
 * 本例用线程t1等待线程t2的下载结果，并将结果输出。t1保持与t2同步。
 *
 * 1、如果一个结果要从一个线程传递到另一个线程，让他们关联同一个GuardedObject
 * 2、如果有结果不断从一个线程到另一个线程，那么可以使用消息队列(见生产者/消费者)
 * 3、jdk中，join、Future的实现，采用的就是此模式
 * 4、因为要等待另一方的结果，因此归类到同步模式
 *
 */
public class GuardedSuspension {
    private static final Logger log = LoggerFactory.getLogger(GuardedSuspension.class);
    private static Guarded guarded = new Guarded();

    public static void main(String[] args) {
        // t1负责等待输出t2下载的内容
        new Thread(()->{
            log.info("{}：等待结果", Thread.currentThread().getName());
            try {
                List<String> content = guarded.getContent();
                log.info("{}得到下载的网页代码行数为：{}",Thread.currentThread().getName(), content.size());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1").start();

        // t2负责下载
        new Thread(()->{
            log.info("{}：执行下载",Thread.currentThread().getName());
            try {
                List<String> content = Downloader.downloader();
                guarded.setContent(content);
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
    }


    static class Guarded {
        //下载的内容,t1,t2共享变量
        private List<String> content;


        public synchronized void setContent(List<String> content){
            this.content = content;
            this.notifyAll();// 唤醒t1,可以输出了
        }

        //wait和notify放在synchronized中有效避免了两线程死锁问题（t1刚准备wait，t2时间片已经notifyAll了，t1再也没有可能被唤醒）
        public synchronized List<String> getContent() throws InterruptedException {
            while(content == null){//2、避免被虚假唤醒
                this.wait();//1、wait()方法必须是关联了Monitor监视器的Java对象去调用，因此必须有同步代码（给方法加上synchronized即可）
            }
            return content;
        }
    }

}
